15 Django之forms组件

481次阅读
没有评论

共计 8431 个字符,预计需要花费 22 分钟才能阅读完成。

一.Django 的 forms 组件应用场景

1. 应用

  • 一般应用于前端的登入、注册界面, 对用户输入的字段进行校验, 快速的判断用户输入的内容是否合法, 并返回信息

2. 为何不在前端直接使用 JS 进行校验

  • 前端的校验可以没有, 但后端的校验必须要有
  • 因为前端的校验弱不禁风, 有很多种方式可以伪装成浏览器发送请求传递数据
  • 或者通过爬虫程序绕过前端页面直接朝后端提交数据

二.forms 组件的基本使用(步骤)

  • 导入 forms 组件
  • 定义一个类, 并继承Form
  • 在类中书写要校验的字段, 字段的属性就是要校验的规则
  • 实例化得到一个 Form 对象, 把要校验的数据传入
  • 调用 [form 对象].is_valid() 方法进行校验, 校验通过返回 **True
  • 校验通过调用 [form 对象].cleaned_data 获得校验后的数据
  • 校验失败调用 [form 对象].errors 获得错误信息

三.forms 组件基本使用示例

简单的用户注册界面示例

1. 常用字段属性

属性 释义
min_length 最小长度
max_length 最大长度
require 该字段必须得填
label 该字段的标签

2. 组件文件 form.py

  • 建议在应用文件夹下创建一个专门存放 forms 组件的文件 (不创建直接写在 views.py 中也可以)

  • 导入组件并创建类

from django import forms


class RegisterForm(forms.Form):
    # 校验 name 字段, 最大长度为 8, 最短为 3, 下面的字段类似
    name = forms.CharField(max_length=8, min_length=3, lable='用户名')
    password = forms.CharField(max_length=10, min_length=4, lable='密码')
    re_password = forms.CharField(max_length=10, min_length=4, lable='确认密码')
    email = forms.EmailField(lable='邮箱')

3. 视图层 views.py 文件

  • 实例 forms 对象, 使用对方法进行校验
from app01.myform import RegisterForm


def register(request):
    if request.method == 'POST':
        # 实例得到 form 对象, 将需要校验的数据传入(数据可以是前端传过来的数据, 也可以是自己后端已有的数据)
        # dic = {'name':'shawn','password':'1111','re_password':'1111','email':'123@qq.com'}
        # form_data = RegisterForm(dic)  # 示例: 直接将已有的数据传入
        form_data = RegisterForm(request.POST)  # 将前端 post 请求的数据传入

        # 进行校验 is_valid()
        if form_data.is_valid():
            print('校验成功')
            # 获得校验成功的数据 cleaned_data
            print(form_data.cleaned_data)
            return HttpResponse('校验成功')
        else:
            print('检验失败')
            # 获得检验失败的错误提示 errors
            print(form_data.errors)

    # forms 组件可以自动生成 HTML 代码,因此可以直接在页面上进行显示
    form_data = RegisterForm()  # 先产生 form 空对象, 再将其传给前端
    return render(request, 're_form.html', {'form': form_data})

ps : cleaned_data属性必须是经过 is_valid() 校验之后才产生的, 在校验之前使用会报错 : 属性不存在

4. 路由层 urls.py 文件

re_path('^re_form/', views.register)

5. 模板层 re_form.html 文件

<div class="container">
    <div class="row">
        <div class="col-md-4">
            <div class="panel panel-info">
                <div class="panel-heading"> 注册页面 </div>
                <div class="panel-body">
                    <form action="" method="post">
                        <p> 用户名:{{form.name}}</p>
                        <p> 密码:{{form.password}}</p>
                        <p> 确认密码:{{form.re_password}}</p>
                        <p> 邮箱:{{form.email}}</p>
                        <br>
                        <input type="submit" class="btn btn-block btn-success" value=" 注册 ">
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

6. 测试效果

  • 检验失败展示

15 Django 之 forms 组件

  • 检验成功展示

15 Django 之 forms 组件

四.forms 组件之渲染标签方式

1. 自动渲染方式一

  • 就是上面示例中的写法
  • 可扩展性很强, 但是需要书写的代码太多
<form action="" method="post">
    <p> 用户名:{{form.name}}</p>
    <p> 密码:{{form.password}}</p>
    <p> 确认密码:{{form.re_password}}</p>
    <p> 邮箱:{{form.email}}</p>
    <br>
    <input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>
  • 也可以使用 label 标签替代前缀(''用户名''、''密码'' 等等)
<form action="" method="post">
    <p>{{form.name.label}}:{{form.name}}</p>
    <p>{{form.password.label}}:{{form.password}}</p>
    <p>{{form.re_password.label}}:{{form.re_password}}</p>
    <p>{{form.email.label}}:{{form.email}}</p>
    <br>
    <input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>

2. 自动渲染方式二(常用方式)

  • 使用 for 循环
  • 代码书写简单, 并且扩展性也高 (推荐使用)
<form action="" method="post" novalidate>
    {% for foo in form %}
        <div class="form-group">
            {# 循环取出 name,password,re_password,email #}
            <p>{{foo.label}}:{{foo}}</p>
        </div>
    {% endfor %}
    <input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>

3. 自动渲染方式三(不常用)

  • 代码书写极少, 封装程度太高, 不便于后续的扩展, 一般情况下只在本地测试使用
<form action="" method="post" novalidate>
    {{form.as_p}}       {# 与上两种显示方式相同 #}
    {{form.as_ul}}      {# 前面带点 #}
    {{form.as_table}}   {# 在一行显示 #}
    <input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>

15 Django 之 forms 组件

五.forms 组件之渲染错误信息

1. 字段参数(举例)

  • required : 是否允许为空,默认 False
  • error_messages : 自定义错误信息
  • widget : 使用 HTML 插件
  • TextInput
  • PasswordInput

2. 代码演示

  • myform.py 文件
from django import forms
from django.forms import widgets

class RegisterForm(forms.Form):
    # 校验 name 字段, 最大长度为 8, 最短为 3, 下面的字段类似
    name = forms.CharField(max_length=8, min_length=3, label='用户名',
                           # 自定义错误信息
                           error_messages={
                               'min_length': '用户名最少 3 位!',
                               'max_length': '用户账户最大 8 位',
                               'required': '用户名不能为空!',
                           },
                           # 添加一个 form-control 类
                           widget=widgets.TextInput(attrs={'class':'form-control'}))
    password = forms.CharField(max_length=10, min_length=4, label='密码',
                               error_messages={
                                   'min_length': '密码最少 4 位!',
                                   'max_length': '密码最大 10 位',
                                   'required': '密码不能为空!',
                               },
                               widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    re_password = forms.CharField(max_length=10, min_length=4, label='确认密码',
                                  error_messages={
                                      'min_length': '密码最少 4 位!',
                                      'max_length': '密码最大 10 位',
                                      'required': '密码不能为空!',
                                  },
                                  widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    email = forms.EmailField(label='邮箱',
                             error_messages={
                                 'invalid': '邮箱格式不正确',
                                 'required': '邮箱不能为空',
                             },
                             widget=widgets.TextInput(attrs={'class':'form-control'}))
  • views.py 文件
from app01.myform import RegisterForm


def register(request):
    if request.method == 'POST':
        form_data = RegisterForm(request.POST)  # 将前端 post 请求的数据传入

        if form_data.is_valid():
            print(form_data.cleaned_data)
            return HttpResponse('校验成功')
        else:
            # 将带有错误信息的 form 对象返回前端页面
            return render(request, 're_form.html', {'form': form_data})

    form_data = RegisterForm()  # 将 form 对象传给前端
    return render(request, 're_form.html', {'form': form_data})
  • re_form.html 文件
<form action="" method="post" novalidate>
    {% for foo in form %}
        {# 在后方设置一个 span 标签用来放错误提示 #}
        <p>{{foo.label}}:{{foo}}
            <span style="color: red">{{foo.errors.0}}</span>
        </p>
    {% endfor %}
    <input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>

ps : <form action=""method="post" novalidate> : form 表单标签内的 novalidate表示前端不进行校验

3. 效果展示

15 Django 之 forms 组件

六.forms 组件之全局钩子, 局部钩子

1. 钩子函数

  • 全局和局部钩子都是定义在 forms 组件之内的函数
  • 钩子函数类似于校验的第二道关卡, 能够让我们自定义校验规则

2. 两种钩子函数

  • 局部钩子 : 用于给单个字段增加校验规则
  • 全局钩子 : 用于给多个字段增加校验规则

3. 两种钩子函数的书写方式

  • 局部钩子
def clean_字段名(self):
    字段值 = self.cleaned_data.get('字段名')
    [判断逻辑代码]
    [不通过抛出 ValidationError 异常]
    [通过则返回该字段值]
  • 全局钩子
def clean(self):
    字段值 1 = self.cleaned_data.get('字段 1')
    字段值 2 = self.cleaned_data.get('字段 2')
    [判断逻辑代码]
    [不通过抛出 ValidationError 异常]
    [通过则 return self.cleaned_data]

4. 钩子示例

  • 需求一 : 校验用户名不能有敏感字眼
  • 需求二 : 校验两次输入的密码需一致

myform.py 新增钩子函数

# 局部钩子
def clean_name(self):
    name = self.cleaned_data.get('name')
    for i in ['爱德华','sb']:
        if i in name:
            raise ValidationError(f'用户名中包含敏感词汇:"{i}"')
    else:
        return name

# 全局钩子
def clean(self):
    password = self.cleaned_data.get('password')
    re_password = self.cleaned_data.get('re_password')
    if password == re_password:
        return self.cleaned_data
    else:
        raise ValidationError('两次密码不一致')

views.py 文件

from app01.myform import RegisterForm


def register(request):
    if request.method == 'POST':
        form_data = RegisterForm(request.POST)  # 将前端 post 请求的数据传入

        if form_data.is_valid():
            return HttpResponse('校验成功')
        else:
            # 获取到 ValidationError 中的 error 信息传给前端
            error = form_data.errors.get('__all__')
            return render(request, 're_form.html', {'form': form_data,'error':error})

    form_data = RegisterForm()  # 将 form 空对象传给前端
    return render(request, 're_form.html', {'form': form_data})

re_form.html 文件

<form action="" method="post" novalidate>
{% for foo in form %}
    <p>{{foo.label}}:{{foo}}
        <span style="color: red; display:inline-block">{{foo.errors.0}}</span>
    </p>
{% endfor %}

<input type="submit" class="btn btn-block btn-success" value=" 注册 ">
{# 判断错误信息是否为 None, 是 None 就不渲染 error #}
{% if error is None %}
    <span style="color: red; display:inline-block"></span>
{% else %}
  <span style="color: red; display:inline-block">{{error}}</span>
{% endif %}
</form>

需求一演示 :

15 Django 之 forms 组件

需求二演示 :

15 Django 之 forms 组件

七.error 错误提示为何是 ul 标签分析

  • 上面我们在 views.py 文件中写到的, 教研室表打印错误提示信息
else:
    print('检验失败')
    # 获得检验失败的错误提示 errors
    print(form_data.errors)
  • 查看打印结果 :

15 Django 之 forms 组件

为什么是一个 ul 标签格式, 一定是重写了 __str__方法

  • 我们导入 ErrorDict 来查看内部源码
from django.forms.utils import ErrorDict

15 Django 之 forms 组件

发现其内部重写了 __str__ 方法, 返回的是 as_ul方法, 该方法内部就是定义了 html 的格式并返回

并且还有一些其他的方法 : 比如 json 格式, data 数据, text 格式等等

而返回 as_ul 方法则是为了方便 渲染模板

八. 常用字段及插件

1. 初始值 : initial

username = forms.CharField(
        min_length=8,
        label=" 用户名 ",
        initial="shawn"  # 设置默认值
    )

2. 自定义错误提示 : error_massages

username = forms.CharField(
        min_length=8,
        label=" 用户名 ",
        initial="shawn",
        error_messages={
            "required": " 不能为空 ",
            "invalid": " 格式错误 ",
            "min_length": " 用户名最短 8 位 "
        }
    )

3. 密码框 : password

# 设置 render_value 在输入错误的时候, 保留输入的内容 value 值
pwd = forms.CharField(
        min_length=4,
        label=" 密码 ",
        widget=forms.widgets.PasswordInput(attrs={'class': 'a1'}, render_value=True)
    )

4. 反选 radio

gender = forms.fields.ChoiceField(choices=((1, " 男 "), (2, " 女 "), (3, " 未知 ")),
    label=" 性别 ",
    initial=3,
    widget=forms.widgets.RadioSelect())

5. 单选 checkbox

keep = forms.ChoiceField(
    label=" 是否记住密码 ",
    initial="checked",
    widget=forms.widgets.CheckboxInput())

6. 多选 checkbox

Copyhobbies1 = forms.MultipleChoiceField(choices=((1, "read"), (2, "run"), (3, "play"),),
    label=" 爱好 ",
    initial=[1, 3],
    widget=forms.widgets.CheckboxSelectMultiple())

7. 单选 Select

Copyeducation  = forms.ChoiceField(choices=((1, " 高中 "), (2, " 中专 "), (3, " 大专 "), (4, " 本科 "), (5, " 研究生 "), (6, " 硕士 ")),
    label=" 学历 ",
    initial=3,
    widget=forms.widgets.Select(attrs={'class': 'form-control'})
)

8. 多选 Select

Copyhobbies2 = forms.MultipleChoiceField(choices=((1, " 篮球 "), (2, " 足球 "), (3, " 双色球 "),),
        label=" 爱好 ",
    initial=[1, 2],
    widget=forms.widgets.SelectMultiple(attrs={'class': 'form-control'})
)

9. 更多插件(了解)

插件名称 对应 input
TextInput input type="text"
PasswordInput input type="password"
HiddenInput input type="hidden"
NumberInput input type="number"
EmailInput input type="email"
URLInput input type="url"
Textarea textarea
DateInput input type="data"
DateTimeInput input type="datetime"
TimeInput 只获取时分秒
CheckboxInput input type="checkbox"
CheckboxSelectMultiple 多选按钮
Select select 框
NullBooleanSelect select 框 三个选项 0 1 null
SelectMultiple 可多选的 select 框
RadioSelect input type="radion"
FileInput 可以查看当前目录下的所有文件。作用不大
正文完
 
shawn
版权声明:本站原创文章,由 shawn 2023-06-16发表,共计8431字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)